package com.retry.task.core.utils.threadPool;

import java.util.concurrent.Callable;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;

import org.apache.commons.lang3.concurrent.BasicThreadFactory;

/**
 * @Author: gaoweiqiang
 * @email: gao.gwq@alibaba-inc.com
 * @Date: 2019/11/15 2:43 下午
 * @Version: 1.0
 * @Description: TODO
 */
public class RetryTaskThreadPoolFactory {

    private RetryTaskThreadPoolFactory() {
    }

    public static final Executor CACHE_POOL = Executors.newCachedThreadPool(
        new BasicThreadFactory.Builder().namingPattern("cache-pool").build());

    private static final LinkedBlockingQueue<Runnable> BLACKING_QUEUE = new LinkedBlockingQueue<Runnable>();
    public static final RetryTaskThreadPoolExector THREAD_POOL_EXECTOR = new RetryTaskThreadPoolExector(16, 32, 60,
        TimeUnit.SECONDS, BLACKING_QUEUE, new BasicThreadFactory.Builder().namingPattern("retry-task").build());

    public static <T> Future<T> submit(Callable<T> task) {
        return THREAD_POOL_EXECTOR.submit(task);
    }

    public static void execute(Runnable runnable) {
        THREAD_POOL_EXECTOR.execute(runnable);
    }

    public static void execute(Runnable runnable, Function function) {
        if (function != null) {
            function.apply(runnable);
        }
        THREAD_POOL_EXECTOR.execute(runnable);
    }

    public static void executeWithEyeConext(Runnable runnable, Object context) {
        RetryTaskRunable mmcRunable = new RetryTaskRunable(context, runnable);
        THREAD_POOL_EXECTOR.execute(mmcRunable);
    }

    public static <V> Future<V> submitWithEyeContext(Callable<V> callable, Object context) {
        RetryTaskCallable mmcCallable = new RetryTaskCallable(context, callable);
        return THREAD_POOL_EXECTOR.submit(mmcCallable);
    }

    public static CompletableFuture<Void> runAsync(Runnable runnable,
        ThreadPoolExecutor threadPoolExecutor, Object context) {
        RetryTaskRunable mmcRunable = new RetryTaskRunable(context, runnable);
        if (threadPoolExecutor == null) {
            threadPoolExecutor = THREAD_POOL_EXECTOR;
        }
        return CompletableFuture.runAsync(mmcRunable, threadPoolExecutor);
    }

    public static <T> CompletableFuture<T> supplyAsync(Callable<T> callable,
        ThreadPoolExecutor threadPoolExecutor, Object context) {
        RetryTaskCallable mmcRunable = new RetryTaskCallable(context, callable);
        if (threadPoolExecutor == null) {
            threadPoolExecutor = THREAD_POOL_EXECTOR;
        }
        return CompletableFuture.supplyAsync(() -> {
            try {
                return (T)mmcRunable.call();
            } catch (Exception ex) {
                throw new RuntimeException(ex);
            }

        }, threadPoolExecutor);
    }
}
